$.widget("ui.Validator", {
    options:
        messages: {}
        rules: {}
        name: ''
        errors: {}
        div: null

    _create: ->
        @SetRules()
        form = $(@).closest("form")
        @options.div = $(@element).closest("fieldset > div")
        that = @

        if !_.isEmpty(@options.rules)
            @options.div.find("input[name='#{ @options.name }']:not([type=text]), select[name='#{ @options.name }']").on("change", ->
                that.Validate($(@))
            )

            lazyValidate = _.debounce(->
                that.Validate($(@))
            500)

            @options.div.find("input[name='#{ @options.name }'], textarea[name='#{ @options.name }']").on("textchange", lazyValidate)
            @options.div.find("input.hasDatepicker[name='#{ @options.name }']").on("change", lazyValidate)

            @options.div.bind("validate", (e, isRef) => @Validate(@element, isRef))

            @options.div.on('mouseenter', (event) ->
                if $(@).data("error-message")?
                    $(@).closest("form").append("<span class='validator'><span>#{ $(@).data('error-message') }</span></span>")
                    validator = $(@).closest("form").find(".validator")
                    height = $(@).height();
                    top = $(@).position().top;
                    left = $(@).position().left;
                    validator.css("top", "#{ top + height }px")
                    validator.css("left", "#{ left }px")
                    return
            )
            @options.div.on('mouseleave', (event) ->
                $(@).closest("form").find(".validator").remove()
            )
            @Validate($(@element))

    Validate: (element, isRef) ->
        if @options.rules.required
            @isRequired($(element)) 
        if @options.rules.number
            @isNumber($(element)) 
        if @options.rules.maxlength
            @isLengthMax($(element), @options.rules.maxlength) 
        if @options.rules.minlength
            @isLengthMin($(element), @options.rules.minlength) 
        if @options.rules.max
            @isRangeMax($(element), @options.rules.max)
        if @options.rules.min
            @isRangeMin($(element), @options.rules.min) 
        if @options.rules.equalTo
            @isEqual($(element), @options.rules.equalTo, isRef) 
        if @options.rules.number
            @isNumber($(element), @options.rules.number) 
        if @options.rules.pattern
            @isRegex($(element), @options.rules.pattern) 
        if @options.rules.remote
            @isRemote($(element), @options.rules.remote, isRef)

        @ShowErrors(element)

    ShowErrors: (element) ->
        errors = ''
        _.each(@options.errors, (error) =>
            if error?
                errors = errors + "<p>#{error}</p>"
        )
        if errors.length
            @options.div.data("error-message",errors)
            @options.div.addClass("error")
        else
            @options.div.removeData("error-message")
            @options.div.removeClass("error")

    SetRules: ->
        element = $(@element)
        name = element.attr("name")
        @options.name = name
        if !@options.rules
            @options.rules = {}
            @options.messages = {}

        #Required
        required = element.data("val-required")
        if (required)
            @options.rules["required"] = true
            @options.messages["required"] = required

        #Url
        url = element.data("val-website")
        if url
            @options.rules["url"] = true
            @options.messages["url"] = url

        #maxLength
        maxLength = element.data("val-length-max")
        if maxLength
            @options.rules["maxlength"] = maxLength
            @options.messages["minlength"] = element.data("val-length")
            @options.messages["maxlength"] = element.data("val-length")

        #minLength
        minLength = element.data("val-length-min")
        if minLength
            @options.rules["minlength"] = minLength
            @options.messages["minlength"] = element.data("val-length")
            @options.messages["maxlength"] = element.data("val-length")

        #Number
        number = element.data('val-number')
        if number
            @options.rules["number"] = true
            @options.messages["number"] = number

        #Max range
        maxRange = element.data("val-range-max")
        if maxRange
            @options.rules["max"] = maxRange

        #Min range
        minRange = element.data("val-range-min")
        if minRange
            @options.rules["min"] = minRange
            @options.messages["max"] = element.data("val-range")
            @options.messages["min"] = element.data("val-range")

        #Regex
        pattern = element.data("val-regex-pattern")
        if pattern
            @options.rules["pattern"] = pattern
            @options.messages['pattern'] = element.data("val-regex")

        #Equal to
        equalTo = element.data("val-equalto-other")
        if equalTo
            @options.rules["equalTo"] = equalTo.replace('*.', '')
            @options.messages["equalTo"] = element.data("val-equalto")           
           
        # Remote
        remote = element.data("val-remote-url")
        if remote
            @options.rules["remote"] = {}
            @options.rules["remote"]["url"] = remote
            @options.rules["remote"]["type"] = "post"
            @options.messages["remote"] = element.data("val-remote")
            data = $(element).data("val-remote-additionalfields").replace(/\*\./g, '')
            if data
                @options.rules["remote"]["data"] = data   

    validate: (validateName, errorCondition) ->
        if errorCondition()
            @options.errors[validateName] = @options.messages[validateName]
        else
            @options.errors[validateName] = null

    isRequired: (element) ->
        if element.hasClass("multiselect")
            @validate( "required", -> element.closest("div").find("input[name=#{ element.attr('Id') }]").length is 0 )
        else
            @validate("required", ->
                if element.val()?
                    if element.val().length == 0
                        return true
                    if element.val() == "0"
                        return true
                return false
            )

    isNumber: (element) ->
        @validate( "number", -> !/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(element.val()) )

    isLengthMax: (element, value) ->
        @validate( "maxlength", -> element.val()?.length > value )

    isLengthMin: (element, value) ->
        @validate( "minlength", ->  element.val()?.length < value )

    isRangeMax: (element, value) ->
        @validate( "max", -> element.val()?.length > value )

    isRangeMin: (element, value) ->
        @validate( "min", -> element.val()?.length < value )

    isEqual: (element, value, isRef) ->
        compareElement = $("[name=#{ value }]");
        if element.val() != compareElement.val()
            @options.errors["equalTo"] = @options.messages["equalTo"]
        else
            @options.errors["equalTo"] = null
        if not isRef
            compareElement.closest("fieldset>div").trigger("validate", true)
            return

    isRegex: (element, pattern) ->
        @validate( "pattern", -> !new RegExp(pattern).test(element.val()))

    isRemote: (element, remote, isRef) ->
        parentDiv = $(element).closest("fieldset>div")
        if parentDiv.length
            $(parentDiv).addClass("remote-start")
            name = $(element).attr("name")
            keys = remote.data.split(',')
            data = ''
            if keys.length == 0
                return false
            for key in keys
                controls = $(element).closest('form').find(":not(select)[name='#{ key }']")
                if controls.length > 1
                    valuesArray = []
                    for control in controls 
                        data = data + key + "=" + $(control).val() + "&"
                else
                    if(controls.length == 1)
                        data = data + key + "=" + $(controls).val() + "&"
                    if(controls.length == 0)
                        data = data + key + "=" + "0" + "&"

            $.ajax(
                type: remote.type
                url: remote.url
                data: data
                success: (response) =>
                    if response
                        @options.errors["remote"] = null
                    else
                        @options.errors["remote"] = @options.messages["remote"]
                    if not isRef       
                        keys = (key for key in keys when key != name)         
                        _.each(keys, (key) =>
                           remoteDiv = $("[name=#{ key }]").closest("fieldset>div")
                           remoteDiv.trigger("validate", true)
                        )
                    @ShowErrors(@element)
                    $(parentDiv).removeClass("remote-start")
                    return
            )
        return
})